/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package action;

import collection.Products;
import com.opensymphony.xwork2.ActionContext;
import dao.OrderDAO;
import dao.OrderStatusDAO;
import dao.ProfileDAO;
import dao.UserDAO;
import dto.Order;
import dto.OrderItems;
import dto.OrderStatus;
import dto.Product;
import dto_pakinfo.Profile;
import dto_pakinfo.User;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.JAXBContext;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.hibernate.HibernateException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import util.ServerValidation;
import util.StringUtil;
import util.XMLUtil;

/**
 *
 * @author HungDD
 */
public class OrderController extends BaseController implements ServletRequestAware {

    private Product product;
    private List<OrderStatus> orderStatusList;
    private OrderDAO orderDAO;
    private Order order;
    private Profile profile;
    private Collection<Order> orders;
    private String cartXML;
    private String customCartXML;
    private String note;
    private Map session;
    private Log log = LogFactory.getLog(OrderController.class);
    private boolean found;
    private InputStream inputStream;
    private HttpServletRequest request;

    public OrderController() {
        orderDAO = new OrderDAO(Order.class);
    }

    /**
     * Load orders in order management page for admin.
     * @return
     */
    public String load_orders() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else if (ServerValidation.isCustomer()) {
            return StringUtil.INVALID_ROLE;
        } else {
            OrderStatusDAO orderStatusDAO = new OrderStatusDAO(OrderStatus.class);
            try {
                orderDAO.openSession();
                orders = orderDAO.getAll();

                orderStatusDAO.openSession();
                orderStatusList = orderStatusDAO.all();
            } catch (Exception ex) {
                log.error("OrderController.load_orders()", ex);
                return ERROR;
            } finally {
                try {
                    orderDAO.closeSession();
                    orderStatusDAO.closeSession();
                } catch (HibernateException ex) {
                    log.error("Can't close session", ex);
                }
            }
            return SUCCESS;
        }
    }

    /**
     * Update an order using AJAX from client
     * @return "success" response if update in server is ok, "fail" otherwise.
     */
    public String updateOrderAJAX() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else if (ServerValidation.isCustomer()) {
            return StringUtil.INVALID_ROLE;
        } else {
            OrderStatusDAO orderStatusDAO = new OrderStatusDAO(OrderStatus.class);
            try {
                int orderId = Integer.parseInt(request.getParameter("orderId"));
                int orderStatusId = Integer.parseInt(request.getParameter("orderStatusId"));

                orderDAO.openSession();
                Order order = orderDAO.get(orderId);

                orderStatusDAO.openSession();
                OrderStatus orderStatus = orderStatusDAO.get(orderStatusId);
                order.setOrderStatus(orderStatus);

                String sDate = request.getParameter("delivereddate");
                if (sDate != null && !sDate.isEmpty()) {
                    DateFormat format = new SimpleDateFormat("yyyy-mm-dd");
                    Date deliveredDate = (Date) format.parse(sDate);
                    order.setDeliveredDatetime(deliveredDate);
                }

                // update order
                orderDAO.update(order);

                // response output to client
                String response = "success";
                inputStream = new ByteArrayInputStream(response.getBytes("UTF-8"));

            } catch (Exception ex) {
                log.error("OrderController.updateOrderAJAX()", ex);
                // response output to client
                String response = "fail";
                try {
                    inputStream = new ByteArrayInputStream(response.getBytes("UTF-8"));
                } catch (UnsupportedEncodingException ex1) {
                    log.error("OrderController.updateOrderAJAX()", ex);
                }
                return ERROR;
            } finally {
                try {
                    orderDAO.closeSession();
                    orderStatusDAO.closeSession();
                } catch (HibernateException ex) {
                    log.error("Can't close session", ex);
                }
            }
            return SUCCESS;
        }

    }

    /**
     * Search order using AJAX call from client
     * @return an inputStream of custom found order in xml for client handle.
     */
    public String searchOrder() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else if (ServerValidation.isCustomer()) {
            return StringUtil.INVALID_ROLE;
        } else {
            try {
                String confirmation = request.getParameter("confirmation");
                String sStartDate = request.getParameter("fromDate");
                String sEndDate = request.getParameter("toDate");
                String sOrderStatus = request.getParameter("orderStatus");
                orderDAO.openSession();
                orders = orderDAO.searchOrder(confirmation, sStartDate, sEndDate, sOrderStatus);

                // generate xml output string
                if (orders == null || orders.isEmpty()) {
                    inputStream = new ByteArrayInputStream("<result>noresult</result>".getBytes());
                    return SUCCESS;
                } else {
                    StringBuilder sb = new StringBuilder();
                    sb.append("<orders>");
                    String orderTagId;
                    String mmddyyyy;
                    SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss");
                    for (Order order : orders) {
                        orderTagId = "<order id=\"" + order.getId() + "\">";
                        sb.append(orderTagId);
                        sb.append("<confirmation>").append(order.getConfirmationString()).append("</confirmation>");
                        sb.append("<user>").append(order.getUser().getUsername()).append("</user>");

                        Date date = order.getPlacedDatetime();
                        mmddyyyy = sdf.format(date);
                        sb.append("<placedDate>").append(mmddyyyy).append("</placedDate>");
                        date = order.getDeliveredDatetime();
                        if (date != null) {
                            mmddyyyy = sdf.format(date);
                        } else {
                            mmddyyyy = "";
                        }
                        sb.append("<deliveredDate>").append(mmddyyyy).append("</deliveredDate>");
                        sb.append("<amount>").append(order.getSubAmount()).append("</amount>");
                        sb.append("<orderstatus>").append(order.getOrderStatus().getDescription()).append("</orderstatus>");
                        sb.append("</order>");
                    }
                    sb.append("</orders>");

                    inputStream = new ByteArrayInputStream(sb.toString().getBytes("UTF-8"));
                    return SUCCESS;
                }

            } catch (Exception ex) {
                inputStream = new ByteArrayInputStream("<result>error</result>".getBytes());
                log.error("OrderController.searchOrder()", ex);
                return ERROR;
            } finally {
                try {
                    orderDAO.closeSession();
                } catch (HibernateException ex) {
                    log.error("Can't close session", ex);
                }
            }
        }

    }

    /**
     * Get order from id then display in order_detail.jsp.
     * @return
     */
    public String orderDetail() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else if (ServerValidation.isCustomer()) {
            return StringUtil.INVALID_ROLE;
        } else {
            int orderId = Integer.parseInt(request.getParameter("orderId"));
            orderDAO.openSession();
            order = orderDAO.get(orderId);

            return SUCCESS;
        }
    }

    public String history() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else if (ServerValidation.isAdmin()) {
            return StringUtil.INVALID_ROLE;
        } else {
            loadSideBar();
            try {
                session = ActionContext.getContext().getSession();
                int userId = (Integer) session.get(StringUtil.USERID);
                orderDAO.openSession();
                orders = orderDAO.getOrdersHistory(userId);

            } catch (Exception ex) {
                log.error("OrderController.history()", ex);
                return ERROR;
            } finally {
                try {
                    orderDAO.closeSession();
                } catch (HibernateException ex) {
                    log.error("Can't close session", ex);
                }
            }
            return SUCCESS;
        }
    }

    public String history_detail() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else if (ServerValidation.isAdmin()) {
            return StringUtil.INVALID_ROLE;
        } else {
            loadSideBar();
            try {
                int orderId = Integer.parseInt(request.getParameter("orderId"));
                orderDAO.openSession();
                order = orderDAO.get(orderId);
            } catch (Exception ex) {
                log.error("OrderController.history()", ex);
                return ERROR;
            }
            return SUCCESS;
        }
    }

    /*
     * Handle when user fill personal information then click submit checkout.
     */
    public String submited_checkout() {
        System.out.println("====>CustomCartXML: " + getCustomCartXML());
        session = ActionContext.getContext().getSession();
        if (ServerValidation.isGuest()) {
            session.put(StringUtil.CARTXML, cartXML.trim());
            session.put(StringUtil.CUSTOMCARTXML, customCartXML.trim());
            return LOGIN;
        } else if (ServerValidation.isAdmin()) {
            addActionError("Just registered user can do this function!");
            return StringUtil.INVALID_ROLE;
        } else {
            try {
                if (cartXML == null || cartXML.isEmpty()) {
                    cartXML = (String) session.get(StringUtil.CARTXML);
                }

                // using customCartXML from client to transform with confirmCheckout.xsl to
                // display in confirmation.jsp page
                if (customCartXML == null || customCartXML.isEmpty()) {
                    customCartXML = (String) session.get(StringUtil.CUSTOMCARTXML);
                }

                // .remove first ',' for no reason lol.
                cartXML = cartXML.trim();
                int index = cartXML.indexOf(',');
                int length = cartXML.length();
                while (index == 0) {
                    cartXML = cartXML.substring(1, length);
                    cartXML = cartXML.trim();
                    index = cartXML.indexOf(',');
                    length = cartXML.length();
                }

                // .remove last ',' for no reason lol.
                cartXML = cartXML.trim();
                while (index == (length - 1)) {
                    cartXML = cartXML.substring(0, index);
                    cartXML = cartXML.trim();
                    index = cartXML.lastIndexOf(',');
                    length = cartXML.length();
                }

                System.out.println(cartXML + "  ++++++");

                StringReader sr = new StringReader(cartXML.trim());
                JAXBContext context = JAXBContext.newInstance(Products.class);
                Object objClient = XMLUtil.unmarshalObject(context, sr);
                String filePath = System.getProperty("rootPath") + util.StringUtil.XML_FOLDER_PATH + "products.xml";
                Object objServer = XMLUtil.unmarshalObject(context, filePath);

                Products clientItems = new Products();
                Products serverItems = new Products();

                if (objClient != null && objServer != null) {
                    clientItems = (Products) objClient;
                    serverItems = (Products) objServer;

                    List<String> errors = ServerValidation.validateCheckoutProcesscing(clientItems, serverItems);

                    if (errors.size() > 0) {
                        for (String s : errors) {
                            addActionError(s);
                        }
                        return INPUT;
                    }
                }

                // validate ok, save ordered item to db.
                UserDAO userDAO = new UserDAO(User.class);
                OrderStatusDAO orderStatusDAO = new OrderStatusDAO(OrderStatus.class);
                session = ActionContext.getContext().getSession();
                int userId = (Integer) session.get(StringUtil.USERID);

                // get user
                userDAO.openSession();
                User user = userDAO.get(userId);
                userDAO.closeSession();

                // set update information for profile
                ProfileDAO profileDAO = new ProfileDAO(Profile.class);
                profileDAO.openSession();
                Profile pr = profileDAO.get(user.getId());
                pr.setFirstname(profile.getFirstname());
                pr.setLastname(profile.getLastname());
                pr.setEmail(profile.getEmail());
                pr.setAddress(profile.getAddress());
                pr.setPhoneNumber(profile.getPhoneNumber());
                profileDAO.update(pr);
                profileDAO.closeSession();


                // get order status
                orderStatusDAO.openSession();
                OrderStatus status = (OrderStatus) orderStatusDAO.getByName(StringUtil.WAITING);
                status.setOrders(null);
                orderStatusDAO.closeSession();

                List<OrderItems> orderItemses = new ArrayList<OrderItems>();
                OrderItems tmp;

                // save order to db
                orderDAO.openSession();
                orderDAO.getSession().beginTransaction();
                Order order = new Order();
                order.setUser(user);
                order.setOrderStatus(status);

                Product updatedProduct = null;
                int newQuantity;
                for (Product p : clientItems.getProducts()) {
                    tmp = new OrderItems();
                    tmp.setOrder(order);
                    tmp.setQuantity(p.getQuantity());
                    updatedProduct = serverItems.contains(p.getId());

                    // update product id for images because constraint fk key
                    if (updatedProduct.getImages() != null) {
                        for (int i = 0; i < updatedProduct.getImages().size(); i++) {
                            updatedProduct.getImages().get(i).setProduct(updatedProduct);
                        }
                    }

                    newQuantity = updatedProduct.getQuantity() - p.getQuantity();
                    updatedProduct.setQuantity(newQuantity);
                    tmp.setPrice(updatedProduct.getPrice());
                    tmp.setProduct(updatedProduct);
                    orderItemses.add(tmp);
                }

                order.setOrderItemses(orderItemses);
                order.setNote(note);
                double subAmount = 0;
                for (int i = 0; i < orderItemses.size(); i++) {
                    subAmount += orderItemses.get(i).getPrice() * orderItemses.get(i).getQuantity();
                }
                order.setSubAmount(subAmount);
                orderDAO.saveOrUpdate(order);

                session.put("orderconfirmation", order.getConfirmationString());

                // update products.xml file
                Document doc;
                for (Product p : clientItems.getProducts()) {
                    found = false;
                    updatedProduct = serverItems.contains(p.getId());
                    newQuantity = updatedProduct.getQuantity();
                    doc = XMLUtil.parserDOMXML(filePath);
                    updateProductQuantityXML(doc, p.getId(), newQuantity);
                    if (found) {
                        XMLUtil.writeXML(doc, filePath);
                    }
                }

                return SUCCESS;
            } catch (Exception ex) {
                log.error("OrderController.submited_checkout()", ex);
                if (orderDAO != null
                        && orderDAO.getSession() != null
                        && orderDAO.getSession().isOpen()
                        && orderDAO.getSession().getTransaction().isActive()) {
                    orderDAO.getSession().getTransaction().rollback();
                }

                return ERROR;
            } finally {
                if (orderDAO != null) {
                    try {
                        orderDAO.closeSession();
                    } catch (HibernateException ex) {
                        log.error("Can't close exception in OrderController.submited_checkout()", ex);
                    }
                }
            }

        }
    }

    /**
     * Display checkout information in pdf format.
     * @return
     */
    public String viewCheckoutPDF() {
        if (ServerValidation.isGuest() || ServerValidation.isAdmin()) {
            return StringUtil.INVALID_ROLE;
        }
        UserDAO userDAO = new UserDAO(User.class);
        try {
            int userId = (Integer) ActionContext.getContext().getSession().get(StringUtil.USERID);

            // get Profile from current user
            userDAO.openSession();
            User user = userDAO.get(userId);
            profile = user.getProfile();

            orderDAO.openSession();

            String path = System.getProperty("rootPath");
            String xslPath = path + StringUtil.XSL_FOLDER_PATH + "orderFO.xsl";
            session = ActionContext.getContext().getSession();
            String xmlString = XMLUtil.generateCheckoutXML(profile, customCartXML, (String) session.get("orderconfirmation"));
            String foPath = path + StringUtil.XML_FOLDER_PATH + "checkoutFO.fo";
            XMLUtil.methodTrAX(xslPath, xmlString, foPath);

            File file = new File(foPath);

            // load user config for font
            String newconfigPath = path + StringUtil.XSL_FOLDER_PATH + "fop.xconf";
            File newconfig = new File(newconfigPath);
            
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            FopFactory ff = FopFactory.newInstance();
            FOUserAgent fua = ff.newFOUserAgent();
            ff.setUserConfig(newconfig);
            Fop fop = ff.newFop(MimeConstants.MIME_PDF, fua, out);

            TransformerFactory tff = TransformerFactory.newInstance();
            Transformer trans = tff.newTransformer();
            Source source = new StreamSource(file);
            Result result = new SAXResult(fop.getDefaultHandler());
            trans.transform(source, result);

            byte[] bytes = null;
            bytes = out.toByteArray();

            if (bytes != null) {
                inputStream = new ByteArrayInputStream(bytes);
            }
            return SUCCESS;
        } catch (Exception ex) {
            log.error("OrderController.viewCheckoutPDF()", ex);
            return ERROR;
        } finally {
            if (userDAO != null) {
                try {
                    userDAO.closeSession();
                } catch (HibernateException ex) {
                    log.error("Can't close exception in OrderController.submited_checkout()", ex);
                }
            }
        }
    }

    /**
     * Generate checkout pdf file name.
     * @return
     */
    public String getCheckoutFileNamePDF() {
        String orderPDFName = "default_PDF.pdf";
        session = ActionContext.getContext().getSession();
        Object obj = session.get(StringUtil.USERNAME);
        if(obj != null) {
            Date today = new Date();
            String username = (String) obj;
            orderPDFName = username + "_" + today.getTime() + ".pdf";
        }
        return orderPDFName;
    }

    /**
     * Update new quantity of product using DOM.
     * @param node reference from products.xml
     * @param newQuantity
     */
    public void updateProductQuantityXML(Node node, int id, int newQuantity) throws NumberFormatException {
        if (node == null || found) {
            return;
        }

        if (node.getNodeName().equals("product")) {
            Node attrProductId = node.getAttributes().getNamedItem("id");
            int productId = Integer.parseInt(attrProductId.getNodeValue());
            if (productId == id) {
                NodeList childrenOfProduct = node.getChildNodes();
                for (int i = 0; i < childrenOfProduct.getLength(); i++) {
                    Node tmp = childrenOfProduct.item(i);
                    if (tmp.getNodeName().equals("quantity")) {
                        tmp.setTextContent(newQuantity + "");
                        found = true;
                        return;
                    }
                }
            }
        }

        // recursion
        int i = 0;
        NodeList children = node.getChildNodes();
        while (i < children.getLength()) {
            updateProductQuantityXML(children.item(i++), id, newQuantity);
        }
    }

    /**
     * @return the product
     */
    public Product getProduct() {
        return product;
    }

    /**
     * @param product the product to set
     */
    public void setProduct(Product product) {
        this.product = product;
    }

    /**
     * @return the orders
     */
    public Collection<Order> getOrders() {
        return orders;
    }

    /**
     * @param orders the orders to set
     */
    public void setOrders(Collection<Order> orders) {
        this.orders = orders;
    }

    /**
     * @return the cartXML
     */
    public String getCartXML() {
        return cartXML;
    }

    /**
     * @param cartXML the cartXML to set
     */
    public void setCartXML(String cartXML) {
        this.cartXML = cartXML;
    }

    /**
     * @return the note
     */
    public String getNote() {
        return note;
    }

    /**
     * @param note the note to set
     */
    public void setNote(String note) {
        this.note = note;
    }

    /**
     * @return the customCartXML
     */
    public String getCustomCartXML() {
        return customCartXML;
    }

    /**
     * @param customCartXML the customCartXML to set
     */
    public void setCustomCartXML(String customCartXML) {
        this.customCartXML = customCartXML;
    }

    /**
     * @return the inputStream
     */
    public InputStream getInputStream() {
        return inputStream;
    }

    /**
     * @param inputStream the inputStream to set
     */
    public void setInputStream(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    /**
     * @return the orderStatusList
     */
    public List<OrderStatus> getOrderStatusList() {
        return orderStatusList;
    }

    /**
     * @param orderStatusList the orderStatusList to set
     */
    public void setOrderStatusList(List<OrderStatus> orderStatusList) {
        this.orderStatusList = orderStatusList;
    }

    public void setServletRequest(HttpServletRequest hsr) {
        this.request = hsr;
    }

    /**
     * @return the order
     */
    public Order getOrder() {
        return order;
    }

    /**
     * @param order the order to set
     */
    public void setOrder(Order order) {
        this.order = order;
    }

    /**
     * @return the profile
     */
    public Profile getProfile() {
        return profile;
    }

    /**
     * @param profile the profile to set
     */
    public void setProfile(Profile profile) {
        this.profile = profile;
    }
}
